/*
 * tcp_input.c
 *
 *  Created on: 2009-5-14
 *      Author: tian
 */
#include"mytcp.h"

/* if it calls tcp_output for data transport,tcppkg can only be
 * the tcp header but not the whole tcp package
 * timer shoud be added
 * action when state is established should be added
 *
 */

int tcp_input(struct ip_fake_hdr* p_ip_fhdr,void* tcppkg,struct tcpcb *ptr,int event){

	/*get tcpcb*/
	struct sockaddr addr;
	struct tcphdr th,*ptcppkg;
	struct tcpcb *p;
	struct tcpcb *ptcpcb;
	int k,j;
	memset(&addr,0,sizeof(struct sockaddr));
	memset(&th,0,sizeof(struct tcphdr));

	if((p_ip_fhdr==NULL||tcppkg==NULL)&&ptr==NULL){
		fprintf(stderr,"pointer is null in tcp_input\n");
		goto sth_wrong;
	}

	if(event==event_null){
		ptcppkg=(struct tcphdr *)tcppkg;
		//for test
		struct tcphdr th1=*(struct tcphdr*)tcppkg;
		printf("in tcp_input:\n");
		printf("sport:%d,dport:%d,",th1.tcp_sport,th1.tcp_dport);
		printf("sum:%d,seq:%ld,ack:%ld\n",th1.tcp_sum,th1.tcp_seq,th1.tcp_ack);
		printf("syn:%d,ack:%d\n",th1.tcp_hl_flag.syn,th1.tcp_hl_flag.ack);
		//test end
		if(tcp_checksum(p_ip_fhdr,tcppkg)!=0){
			fprintf(stderr,"tcp check sum is wrong in tcp_input\n");
			goto sth_wrong;
		}
		addr.my_ip=p_ip_fhdr->des;
		addr.my_port=(ptcppkg)->tcp_dport;
		addr.peer_ip=p_ip_fhdr->src;
		addr.peer_port=(ptcppkg)->tcp_sport;
	}
	if(ptr==NULL){
		ptcpcb=get_tcpcb(&addr);
		if(ptcpcb==NULL){
			memset(&addr.peer_ip,0,sizeof(addr.peer_ip));
			addr.peer_port=0;
			ptcpcb=get_tcpcb(&addr);
		}
		if(ptcpcb==NULL){
			fprintf(stderr,"tcpcb is null in tcp_input\n");
			goto sth_wrong;
		}
	}
	else
		ptcpcb=ptr;

	//for test
	printf("tcpcb: pid:%ld,sock:%d,state:%d\n",ptcpcb->pid,ptcpcb->sock,ptcpcb->tcp_state);
	//test end

	/*handle as tcp state*/
	switch(ptcpcb->tcp_state){
	case TCP_CLOSED:
		if(event==active_open){
			//(ptcppkg)->tcp_hl_flag.syn=1;
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.syn=1;
			ptcpcb->tcp_state=TCP_SYN_SENT;
			tcp_output(&th,NULL,0,ptcpcb);
			/*set timer*/
			ptcpcb->timer[0]=10;
			goto success;
		}
		else if(event==passive_open){
			ptcpcb->tcp_state=TCP_LISTEN;
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_LISTEN:
		if(event==event_null&&(ptcppkg)->tcp_hl_flag.syn==1){
			/*create a new tcpcb*/
			addr.peer_ip=p_ip_fhdr->src;
			addr.peer_port=(ptcppkg)->tcp_sport;
			if((p=tcp_new_tcpcb(ptcpcb->pid,0,TCP_SYN_RECV,&addr,ptcpcb->msgqid))==NULL)
				goto sth_wrong;
			p->peerseq=(ptcppkg)->tcp_seq+1;
			/*send ACK&SYN package*/
			//p->sockaddr.peerprot...
			th.tcp_dport=(ptcppkg)->tcp_sport;
			th.tcp_sport=(ptcppkg)->tcp_dport;
			th.tcp_ack=(ptcppkg)->tcp_seq+1;
			th.tcp_hl_flag.ack=1;
			th.tcp_hl_flag.syn=1;
			tcp_output(&th,NULL,0,p);
			/*set timer*/
			p->timer[0]=10;
			goto success;

		}
		else if(event==send_data){
			ptcpcb->tcp_state=TCP_SYN_SENT;
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.syn=1;
			ptcpcb->tcp_state=TCP_SYN_SENT;
			tcp_output(&th,NULL,0,ptcpcb);
			/*set timer*/
			ptcpcb->timer[0]=10;
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_SYN_SENT:
		if(event==event_null&&(ptcppkg)->tcp_hl_flag.syn==1&&(ptcppkg)->tcp_hl_flag.ack==1){
			/*check ack*/
			if((ptcppkg)->tcp_ack!=ptcpcb->seq)
				goto sth_wrong;
			/*clear timer*/
			ptcpcb->timer[0]=0;
			ptcpcb->timer[1]=0;
			ptcpcb->tcp_state=TCP_ESTABLISHED;
			ptcpcb->peerseq=(ptcppkg)->tcp_seq+1;
			/*send ACK package*/
			//p->sockaddr.peerprot...
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_ack=ptcpcb->peerseq;
			th.tcp_hl_flag.ack=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto success;

		}
		else if(event==event_null&&(ptcppkg)->tcp_hl_flag.syn==1){
			memset(&addr.peer_ip,0,sizeof(struct in_addr));
			addr.peer_port=0;
			struct tcpcb *p=ptcpcb;
			ptcpcb=get_tcpcb(&addr);
			if(ptcpcb==NULL)
				goto sth_wrong;
			p->peerseq=(ptcppkg)->tcp_seq+1;
			/*send ACK&SYN package*/
			//p->sockaddr.peerprot...
			th.tcp_dport=(ptcppkg)->tcp_sport;
			th.tcp_sport=(ptcppkg)->tcp_dport;
			th.tcp_ack=(ptcppkg)->tcp_seq+1;
			th.tcp_hl_flag.ack=1;
			th.tcp_hl_flag.syn=1;
			tcp_output(&th,NULL,0,p);
			goto success;
		}
		else if(event==close_sock||event==timeout){
			ptcpcb->tcp_state=TCP_CLOSED;
			goto success;
		}
		else
			goto sth_wrong;
	}
	//check peer seq
	//i do not konw peerseq++ right or wrong
	if(event==event_null)
		if((ptcppkg)->tcp_seq!=ptcpcb->peerseq++)
			goto sth_wrong;

	switch(ptcpcb->tcp_state){
	case TCP_SYN_RECV:
		if(event==event_null&&(ptcppkg)->tcp_hl_flag.ack==1){
			if((ptcppkg)->tcp_ack!=ptcpcb->seq)
				goto sth_wrong;
			/*clear timer*/
			ptcpcb->timer[0]=0;
			ptcpcb->timer[1]=0;
			memset(&addr,0,sizeof(struct in_addr));
			addr.peer_port=0;
			p=get_tcpcb(&addr);
			/*check listen waiting queue if is full*/
			if((p->q2end+1)%p->q2num==0)
				goto sth_wrong;
			/*enqueue the request*/
			addr.peer_ip=p_ip_fhdr->src;
			addr.peer_port=(ptcppkg)->tcp_sport;
			p->queue2[p->q2end++]=addr;
			p->q2end=p->q2end%p->q2num;
			ptcpcb->tcp_state=TCP_ESTABLISHED;
			goto recv_data;
		}
		else if(event==close_sock){
			ptcpcb->tcp_state=TCP_FIN_WAIT1;
			/*send FIN package*/
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.fin=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto success;
		}
		else
			goto sth_wrong;

		/* in state established,when event is send data,
		 * must check tcp timer in order to make sure
		 * last ack is recieved
		 */
	case TCP_ESTABLISHED:
		if(event==close_sock){
			ptcpcb->tcp_state=TCP_FIN_WAIT1;
			/*send FIN package*/
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.fin=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto success;
		}
		else if(event==event_null&&(ptcppkg)->tcp_hl_flag.fin==1){
			if((ptcppkg)->tcp_hl_flag.ack==1){
				if((ptcppkg)->tcp_ack!=ptcpcb->seq)
								goto sth_wrong;
				ptcpcb->timer[1]=0;
			}
			ptcpcb->tcp_state=TCP_CLOSE_WAIT;
			/*send FIN package*/
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.fin=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto recv_data;
		}
		else if(event==event_null&&(ptcppkg)->tcp_hl_flag.ack==1){
			if((ptcppkg)->tcp_ack!=ptcpcb->seq)
				goto sth_wrong;
			ptcpcb->timer[1]=0;
			goto recv_data;
		}
		else if(event==event_null){
			goto recv_data;
		}
		else if(event==send_data){
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			tcp_output(&th,ptcpcb->sndbuf,ptcpcb->sndlen,ptcpcb);
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_FIN_WAIT1:
		if(event==event_null&&(ptcppkg)->tcp_hl_flag.fin==1&&(ptcppkg)->tcp_hl_flag.ack==1){
			if((ptcppkg)->tcp_ack!=ptcpcb->seq)
				goto sth_wrong;
			ptcpcb->timer[1]=0;
			ptcpcb->tcp_state=TCP_TIME_WAIT;
			/*set timer*/
			ptcpcb->timer[2]=6;
			/*send ACK package*/
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.ack=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto success;
		}
		else if(event==event_null&&(ptcppkg)->tcp_hl_flag.ack==1){
			if((ptcppkg)->tcp_ack!=ptcpcb->seq)
				goto sth_wrong;
			ptcpcb->timer[1]=0;
			ptcpcb->tcp_state=TCP_FIN_WAIT2;
			goto success;
		}
		else if(event==event_null&&(ptcppkg)->tcp_hl_flag.fin==1){
			ptcpcb->tcp_state=TCP_CLOSING;
			/*send ACK package*/
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.ack=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_FIN_WAIT2:
		if(event==event_null&&(ptcppkg)->tcp_hl_flag.fin==1){
			ptcpcb->tcp_state=TCP_TIME_WAIT;
			ptcpcb->timer[2]=6;
			/*send ACK package*/
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.ack=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_CLOSING:
		if(event==event_null&&(ptcppkg)->tcp_hl_flag.ack==1){
			if((ptcppkg)->tcp_ack!=ptcpcb->seq)
				goto sth_wrong;
			ptcpcb->timer[1]=0;
			ptcpcb->timer[2]=6;
			ptcpcb->tcp_state=TCP_TIME_WAIT;
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_TIME_WAIT:
		if(event==timeout){
			ptcpcb->tcp_state=TCP_CLOSED;
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_CLOSE_WAIT:
		if(event==close_sock){
			ptcpcb->tcp_state=TCP_LAST_ACK;
			/*send FIN package*/
			th.tcp_dport=ptcpcb->sock_addr.peer_port;
			th.tcp_sport=ptcpcb->sock_addr.my_port;
			th.tcp_hl_flag.fin=1;
			tcp_output(&th,NULL,0,ptcpcb);
			goto success;
		}
		else
			goto sth_wrong;
	case TCP_LAST_ACK:
		if(event==event_null&&(ptcppkg)->tcp_hl_flag.ack==1){
			if((ptcppkg)->tcp_ack!=ptcpcb->seq)
				goto sth_wrong;
			ptcpcb->timer[1]=0;
			ptcpcb->tcp_state=TCP_CLOSED;
			goto success;
		}
		else
			goto sth_wrong;
	default:
		goto sth_wrong;
	};

	sth_wrong:
		return -1;

	recv_data:
		j=p_ip_fhdr->tcp_l-sizeof(struct tcphdr);
		for(k=0;k<j;k++)
			if(ptcpcb->recend+k==ptcpcb->recbeg)
				goto sth_wrong;
			else
				ptcpcb->recbuf[ptcpcb->recend+k]=*(char *)(tcppkg+sizeof(struct tcphdr)+k);

	success:
		return 0;
}
